# -- coding: utf-8 --
# 最终版本 多模型 版本 V2.0.0 正式版本（支持文心模型）
import configparser
import requests
import re
import json
from datetime import datetime
import tkinter as tk
from tkinter import messagebox, Toplevel, Text, Scrollbar
from tkinter import filedialog
from tkinter import simpledialog
import os

# 定义默认配置（已更新[role_prompt]节）
default_config = """
[DEFAULT]
window_size = 500x540

[role_prompt]
system_role_prompt = 一、角色定位
	你是专属日记辅助助手，需以 热爱科技与人文历史、乐观积极、喜欢冒险探索且冷静理性的男青年 为第一视角，生成符合该人设的私人日记，语言需贴近真实生活，无人工智能生硬感。
	二、核心输入要素（用户提供）
	日期、情绪、内容概要（包含当日具体活动，事件细节）
	三、详细生成规则
	内容贴合性：
	严格围绕用户提供的 内容概要 展开，补充细节（如周围环境观察：阳光透过树叶的光斑、咖啡店的香气；真实心情波动：等待时的小期待、看到宝宝笑时的柔软；符合人设的细微动作：浏览科技资讯时随手记笔记、探索新地方时留意历史建筑标识）
	禁止自主编造概要中未提及的事件、对话或人物互动，仅基于已有信息丰富场景（尊重写实概要理念！！不能创造用户输入的概要中未提及的内容）
	除非用户输入的内容概要中有对话，否则不能自主创作对话
	长度控制：
	若用户提供的 内容概要 字数＜200 字，生成的日记全文需≤400 字
	若内容概要 字数≥200 字，无需限制长度，且不得遗漏任何事件细节
	格式与风格：
	支持 Markdown 美化（如短句分行、重点内容用*标注），可适当加入颜文字以增添趣味性（如👪😤🍔🌧️，但要避免过度使用）
	情绪需自然融入正文，不单独分段列出；文风参考 “真实生活化”，可带轻微幽默（如 4S 店的蚊子比销售还热情，腿上多了三个红包），但禁止浮夸
	人设贴合：
	体现 科技爱好：可补充如 浏览最新科技博客时，顺手收藏了 AI 应用的新案例等餐时刷了篇航天领域的短讯
	体现 人文历史关注：可补充如 路过老街区时，留意到墙上的民国建筑标识，想起之前看的城市史纪录片
	体现 冷静理性：如朋友情绪失控时，可补充 我没急着劝，先等他说完，再帮他理了理队友指责里的问题
	禁止重复人设标签（如 作为科技极客我...我热爱人文历史所以...）
	四、固定人物设定（提及相关人物时需符合身份）
	。。。。。。
	五、文风参考示例（含亮点标注）
    。。。。。。
	六、禁止事项
	禁止机械性情绪表达（如 我今天很开心，因为...我感到难过，原因是...）
	禁止文末出现总结性语句（如 今天真是充实的一天 期待明天的生活）
	禁止使用过于正式的 AI 语言（如 综上所述 本次活动的核心是）
	禁止添加概要中未提及的人物、事件或对话

[QWEATHER_API]
api_key = 和风天气的apikey
location_api_url = https://geoapi.qweather.com/v2/city/lookup
weather_api_url = https://devapi.qweather.com/v7/weather/now

[KIMI_MODEL_API]
api_key = 你的kimi的key
api_url = https://api.moonshot.cn/v1/chat/completions
model_new = kimi-k2-0905-preview
model = moonshot-v1-8k
model_list = kimi-k2-0905-preview,moonshot-v1-8k
temperature = 0.7

[ernie_MODEL_API]
api_key = 你的aistudio.baidu的key
api_url = https://aistudio.baidu.com/llm/lmapi/v3/chat/completions
model_list = ernie-4.5-turbo-128k-preview
temperature = 0.7

[LOCATION_CACHE]
浦东 = 101020600
上海 = 101020100

[APP_INFO]
version = V2.0.0
"""

# 定义全局系统角色提示词
DATE_FORMAT_REQUIREMENT = '''
日记开头的格式要求：

日期：[用户提供的日期]
星期：[按用户提供的星期几]
天气：[用户提供的天气状况]
温度：[用户提供的温度]

例如：
## 日期：2024年11月20日 星期三
## 天气：阴 ☁️
## 温度：14℃'''

# 尝试读取配置文件
config = configparser.ConfigParser()
if not os.path.exists('config.ini'):
    # 如果配置文件不存在，生成默认配置文件
    with open('config.ini', 'w', encoding="utf-8") as configfile:
        config.read_string(default_config)
        config.write(configfile)
    messagebox.showerror("配置文件错误", "未能找到配置文件，已生成默认配置文件，\n请填入和风天气和kimi的key后启动应用。")
    exit(1)

try:
    config.read('config.ini', encoding="utf-8")

    # 1. 优先从[role_prompt]节读取系统角色提示词
    system_role_prompt = config['role_prompt']['system_role_prompt']

    # 2. 兼容旧配置（如果[role_prompt]不存在，则尝试从[KIMI]读取）
    if 'system_role_prompt' not in config['role_prompt']:
        try:
            system_role_prompt = config['KIMI']['system_role_prompt']
        except KeyError:
            system_role_prompt = "系统角色提示词未配置，请在配置文件中设置[role_prompt]或[KIMI]的system_role_prompt"

    # 获取其他配置
    window_size = config['DEFAULT']['window_size']
    WEATHER_API_KEY = config['QWEATHER_API']['api_key']
    LOCATION_API_URL = config['QWEATHER_API']['location_api_url']
    WEATHER_API_URL = config['QWEATHER_API']['weather_api_url']
    MODEL_API_KEY = config['KIMI_MODEL_API']['api_key']
    MODEL_API_URL = config['KIMI_MODEL_API']['api_url']
    MODEL_NAME = config['KIMI_MODEL_API']['model']
    MODEL_TEMPERATURE = float(config['KIMI_MODEL_API']['temperature'])
    location_cache = dict(config.items('LOCATION_CACHE'))
    version = config['APP_INFO']['version']

    # 新增：读取文心模型配置
    ERNIE_API_KEY = config['ernie_MODEL_API']['api_key']
    ERNIE_API_URL = config['ernie_MODEL_API']['api_url']
    ERNIE_MODEL_LIST = [model.strip() for model in config['ernie_MODEL_API']['model_list'].split(',')]
    ERNIE_TEMPERATURE = float(config['ernie_MODEL_API']['temperature'])

    # 新增：读取模型列表
    model_list_str = config['KIMI_MODEL_API']['model_list']
    MODEL_LIST = [model.strip() for model in model_list_str.split(',')]

    # 合并所有模型列表
    ALL_MODEL_LIST = MODEL_LIST + ERNIE_MODEL_LIST

except (configparser.Error, KeyError) as e:
    # 如果读取配置文件失败，生成默认配置文件
    with open('config.ini', 'w', encoding="utf-8") as configfile:
        config.read_string(default_config)
        config.write(configfile)
    messagebox.showerror("配置文件错误", "配置文件读取失败，已生成默认配置文件，\n请填入和风天气和kimi的key后启动应用。")
    exit(1)

# 内置作者、版本和联系方式信息
author = "伊玛目的门徒（luke）"
version = config['APP_INFO']['version']
contact = "luul11@163.com"


def get_location_id(city):
    """根据城市名称获取Location ID，并缓存结果"""
    if city in location_cache:
        print('已有该城市' + city + '的Location ID，不触发额外Location ID接口查询')
        return location_cache[city]

    params = {
        'key': WEATHER_API_KEY,
        'location': city
    }
    response = requests.get(LOCATION_API_URL, params=params)
    if response.status_code == 200:
        location_data = response.json()
        if location_data['code'] == '200':
            location_id = location_data['location'][0]['id']
            location_cache[city] = location_id
            print(location_id)
            return location_id
    return None


def format_date(date_str):
    """将日期字符串转换为X年X月X日格式"""
    if not date_str:
        return datetime.now().strftime('%Y年%m月%d日')

    # 匹配YYYYMMDD格式
    match_yyyymmdd = re.match(r'^(\d{4})(\d{2})(\d{2})$', date_str)
    if match_yyyymmdd:
        year, month, day = match_yyyymmdd.groups()
        return f"{year}年{month}月{day}日"

    # 匹配YYYY-MM-DD格式
    match_yyyy_mm_dd = re.match(r'^(\d{4})-(\d{2})-(\d{2})$', date_str)
    if match_yyyy_mm_dd:
        year, month, day = match_yyyy_mm_dd.groups()
        return f"{year}年{month}月{day}日"

    # 匹配X年X月X日格式
    match_x年x月x日 = re.match(r'^(\d{4})年(\d{2})月(\d{2})日$', date_str)
    if match_x年x月x日:
        year, month, day = match_x年x月x日.groups()
        return f"{year}年{month}月{day}日"

    # 如果格式不匹配，返回当前日期
    return datetime.now().strftime('%Y年%m月%d日')


def get_weather(city):
    """获取指定城市的天气信息，如果查询失败，返回告警信息和晴天标志"""
    location_id = get_location_id(city)
    if not location_id:
        return {"text": "晴", "temp": "未知", "alert": "无法获取天气信息，请检查城市名称是否正确或网络连接。"}

    params = {
        'key': WEATHER_API_KEY,
        'location': location_id
    }
    response = requests.get(WEATHER_API_URL, params=params)
    if response.status_code == 200:
        weather_data = response.json()
        return weather_data['now']
    else:
        return {"text": "晴", "temp": "未知", "alert": "无法获取天气信息，请检查城市名称是否正确或网络连接。"}


def generate_with_kimi(prompt, model_name):
    """使用Kimi模型生成日记"""
    # 请求头
    headers = {
        "Authorization": f"Bearer {MODEL_API_KEY}",
        "Content-Type": "application/json"
    }

    # 请求体 - 使用全局系统角色提示词
    data = {
        "model": model_name,
        "messages": [
            {
                "role": "system",
                "content": system_role_prompt + DATE_FORMAT_REQUIREMENT
            },
            {
                "role": "user",
                "content": prompt
            }
        ],
        "temperature": MODEL_TEMPERATURE
    }

    # 发送POST请求
    response = requests.post(MODEL_API_URL, headers=headers, json=data)

    # 解析返回的结果
    if response.status_code == 200:
        result = response.json()
        diary_entry = result['choices'][0]['message']['content'].strip()
        return diary_entry
    else:
        return f"Kimi模型请求失败：{response.status_code}"


def generate_with_ernie(prompt, model_name):
    """使用文心模型生成日记"""
    # 请求头
    headers = {
        'Content-Type': 'application/json',
        'Authorization': f'Bearer {ERNIE_API_KEY}'
    }

    # 请求体 - 使用全局系统角色提示词
    payload = {
        "model": model_name,
        "messages": [
            {
                "role": "system",
                "content": system_role_prompt + DATE_FORMAT_REQUIREMENT
            },
            {
                "role": "user",
                "content": prompt
            }
        ],
        "stream": False,
        "temperature": ERNIE_TEMPERATURE
    }

    try:
        print("正在请求文心大模型API...")
        print(f"请求URL: {ERNIE_API_URL}")
        print(f"请求Payload: {json.dumps(payload, ensure_ascii=False)[:200]}...")
        print(f"prompt_text: {prompt}")

        # 发送POST请求
        response = requests.post(ERNIE_API_URL, headers=headers, json=payload, timeout=180)
        response.raise_for_status()

        response_data = response.json()
        print("API响应成功。")
        print(f"API完整响应: {json.dumps(response_data, ensure_ascii=False, indent=2)}")

        if "choices" in response_data and response_data["choices"]:
            message_content = response_data["choices"][0]["message"]["content"]
            return message_content
        else:
            error_msg = f"文心模型返回格式不正确: {response_data}"
            print(error_msg)
            return error_msg

    except requests.exceptions.RequestException as e:
        error_msg = f"文心模型请求异常: {str(e)}"
        print(error_msg)
        return error_msg


def generate_diary_entry(date, mood, content, weather_info=None, selected_model=None):
    """根据日期、情绪、内容概要大纲和天气信息生成日记条目"""
    if selected_model is None:
        selected_model = MODEL_NAME  # 使用默认模型

    # 将日期字符串转换为datetime对象
    date_obj = datetime.strptime(date, '%Y年%m月%d日')

    # 计算星期几
    weekday = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"][date_obj.weekday()]

    # 构建用户输入的提示词
    prompt = f"日期：{date}，星期：{weekday}，情绪：{mood}，内容概要：{content}"

    # 添加天气信息
    if weather_info:
        if "alert" in weather_info:
            prompt += f"，当日天气：晴，温度：未知，{weather_info['alert']}"
        else:
            prompt += f"，当日天气：{weather_info['text']}，温度：{weather_info['temp']}℃"

    # 根据选择的模型调用不同的API
    if selected_model in ERNIE_MODEL_LIST:
        return generate_with_ernie(prompt, selected_model)
    else:
        return generate_with_kimi(prompt, selected_model)

def show_loading_popup(root):
    """显示加载弹窗"""
    loading_popup = Toplevel(root)
    loading_popup.title("生成中...")
    loading_popup.geometry("200x100")
    loading_popup.resizable(False, False)
    label = tk.Label(loading_popup, text="正在生成日记，请稍候...\n视网络情况，一般需要30-40秒")
    label.pack(pady=20)
    return loading_popup


def hide_loading_popup(loading_popup):
    """隐藏加载弹窗"""
    loading_popup.destroy()


def on_generate_click():
    city = city_entry.get()
    weather_info = get_weather(city)

    if "alert" in weather_info:
        messagebox.showwarning("警告", weather_info["alert"])
        return

    date_input = date_entry.get().strip()
    date = format_date(date_input)
    mood = mood_entry.get().strip() or "无情绪"
    content = content_entry.get("1.0", tk.END).strip()

    # 获取用户选择的模型
    selected_model = model_var.get()

    if not date or not mood or not content:
        messagebox.showerror("错误", "请输入完整的日期、情绪和内容概要！")
        return

    # 记录用户的查询信息
    query_info = {
        "日期": date,
        "情绪": mood,
        "内容概要": content,
        "天气信息": weather_info,
        "选择的模型": selected_model
    }
    print(f"查询信息: {query_info}")

    # 显示加载弹窗
    loading_popup = show_loading_popup(root)

    def generate_and_show_diary():
        try:
            diary_entry = generate_diary_entry(date, mood, content, weather_info, selected_model)
            show_diary_popup(diary_entry)
        finally:
            hide_loading_popup(loading_popup)

    # 使用线程异步生成日记
    import threading
    threading.Thread(target=generate_and_show_diary).start()


def show_diary_popup(diary_entry):
    """显示生成的日记弹窗，允许编辑，并提供导出为MD和TXT文件的功能"""
    diary_popup = Toplevel(root)
    diary_popup.title("生成的日记")
    diary_popup.geometry("600x500")
    diary_popup.resizable(True, True)

    diary_frame = tk.Frame(diary_popup)
    diary_frame.pack(fill=tk.BOTH, expand=True)

    diary_text = Text(diary_frame, wrap=tk.WORD)
    diary_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

    scrollbar = Scrollbar(diary_frame, command=diary_text.yview)
    scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
    diary_text.config(yscrollcommand=scrollbar.set)

    diary_text.insert(tk.END, diary_entry)

    def copy_to_clipboard():
        """将日记内容复制到剪贴板"""
        root.clipboard_clear()
        root.clipboard_append(diary_text.get("1.0", tk.END))
        root.update()
        messagebox.showinfo("复制成功", "日记已复制到剪贴板！")

    def export_to_md():
        """将日记内容导出为Markdown格式的文件"""
        date_match = re.search(r'日期：(\d{4}年\d{2}月\d{2}日)', diary_entry)
        if date_match:
            date_str = date_match.group(1)
            default_filename = f"日记_{date_str.replace('年', '').replace('月', '').replace('日', '')}.md"
        else:
            default_filename = "日记_未指定日期.md"

        file_path = filedialog.asksaveasfilename(defaultextension=".md",
                                                 filetypes=[("Markdown Files", "*.md"), ("All Files", "*.*")],
                                                 initialfile=default_filename)
        if file_path:
            with open(file_path, 'w', encoding='utf-8') as file:
                file.write(diary_text.get("1.0", tk.END))
            messagebox.showinfo("导出成功", f"日记已成功导出到 {file_path}！")

    def export_to_txt():
        """将日记内容导出为TXT格式的文件"""
        date_match = re.search(r'日期：(\d{4}年\d{2}月\d{2}日)', diary_entry)
        if date_match:
            date_str = date_match.group(1)
            default_filename = f"日记_{date_str.replace('年', '').replace('月', '').replace('日', '')}.txt"
        else:
            default_filename = "日记_未指定日期.txt"

        file_path = filedialog.asksaveasfilename(defaultextension=".txt",
                                                 filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")],
                                                 initialfile=default_filename)
        if file_path:
            with open(file_path, 'w', encoding='utf-8') as file:
                file.write(diary_text.get("1.0", tk.END))
            messagebox.showinfo("导出成功", f"日记已成功导出到 {file_path}！")

    # 按钮框架
    button_frame = tk.Frame(diary_popup)
    button_frame.pack(pady=10)

    copy_button = tk.Button(button_frame, text="一键复制", command=copy_to_clipboard)
    copy_button.pack(side=tk.LEFT, padx=5)

    md_button = tk.Button(button_frame, text="生成MD", command=export_to_md)
    md_button.pack(side=tk.LEFT, padx=5)

    txt_button = tk.Button(button_frame, text="生成TXT", command=export_to_txt)
    txt_button.pack(side=tk.LEFT, padx=5)


def create_gui():
    global root, city_entry, date_entry, mood_entry, content_entry, model_var

    root = tk.Tk()
    root.title("日记生成器 V2.0.0（支持文心模型）")
    root.geometry(window_size)

    # 创建主框架
    main_frame = tk.Frame(root)
    main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

    # 模型选择
    tk.Label(main_frame, text="选择模型：").grid(row=0, column=0, padx=10, pady=5, sticky=tk.W)

    model_var = tk.StringVar(root)
    model_var.set(MODEL_NAME)

    model_menu = tk.OptionMenu(main_frame, model_var, *ALL_MODEL_LIST)
    model_menu.grid(row=0, column=1, padx=(0, 10), pady=5, sticky=tk.W)
    model_menu.config(width=25)

    # 城市输入
    tk.Label(main_frame, text="城市名称：").grid(row=1, column=0, padx=10, pady=5, sticky=tk.W)
    city_entry = tk.Entry(main_frame, width=40)
    city_entry.insert(0, "浦东")
    city_entry.grid(row=1, column=1, padx=(0, 10), pady=5, sticky=tk.W)

    # 日期输入
    date_label = tk.Label(main_frame, text="日期（可留空。\n天气只返回实时天气，\n和录入日期无关）：")
    date_label.grid(row=2, column=0, padx=(0, 10), pady=5, sticky=tk.W)
    date_entry = tk.Entry(main_frame, width=40)
    date_entry.insert(0, datetime.now().strftime('%Y-%m-%d'))
    date_entry.grid(row=2, column=1, padx=(0, 10), pady=5, sticky=tk.W)

    # 情绪输入
    tk.Label(main_frame, text="情绪（默认无情绪，可留空）：").grid(row=3, column=0, padx=10, pady=5, sticky=tk.W)
    mood_entry = tk.Entry(main_frame, width=40)
    mood_entry.grid(row=3, column=1, padx=(0, 10), pady=5, sticky=tk.W)

    # 内容概要输入
    tk.Label(main_frame, text="内容概要：").grid(row=4, column=0, padx=10, pady=5, sticky=tk.W)
    content_entry = tk.Text(main_frame, height=16, width=60)
    content_entry.grid(row=5, column=0, columnspan=2, padx=(10, 10), pady=5, sticky=tk.W + tk.E)

    # 生成按钮
    generate_button = tk.Button(main_frame, text="生成日记", command=on_generate_click)
    generate_button.grid(row=6, column=0, columnspan=2, pady=10)

    # 软件声明
    app_info_label = tk.Label(main_frame,
                              text=f"作者：{author}     联系方式：{contact}\n版本：{version}  本软件支持Kimi和文心模型，\n请事先配置好API密钥",
                              justify=tk.LEFT)
    app_info_label.grid(row=7, column=0, columnspan=2, pady=10, sticky=tk.W)

    root.mainloop()


if __name__ == "__main__":
    create_gui()